import { Button, Upload, UploadFile, UploadProps } from "antd";
import React, { useEffect, useRef, useState } from "react";
import { createWorker } from "tesseract.js";
import { UploadOutlined } from "@ant-design/icons";

const Tesseract = () => {
    const [url, setUrl] = useState("https://tesseract.projectnaptha.com/img/eng_bw.png");
    const [file, setFile] = useState<File | null>(null);
    const [res, setRes] = useState("");
    const [fileList, setFileList] = useState<UploadFile[]>([]);
    const prevObjectUrlRef = useRef<string | null>(null);

    const detailOCR = async () => {
        const target = (fileList[0]?.originFileObj as File | undefined) ?? file ?? url;
        const worker = await createWorker("eng");
        const ret = await worker.recognize(target as any);
        console.log(ret.data.text);
        setRes(ret.data.text);
        await worker.terminate();
    };

    const props: UploadProps = {
        accept: "image/*",
        listType: "picture",
        maxCount: 1,
        fileList,
        onChange: (info) => {
            setFileList(info.fileList);
            const f = info.fileList[0]?.originFileObj as File | undefined;
            if (f) {
                const objUrl = URL.createObjectURL(f);
                if (prevObjectUrlRef.current) URL.revokeObjectURL(prevObjectUrlRef.current);
                prevObjectUrlRef.current = objUrl;
                setUrl(objUrl);
                setFile(f);
                setRes("");
            }
        },
        beforeUpload: (f) => {
            // 阻止自动上传，直接本地处理
            setFile(f as unknown as File);
            const objUrl = URL.createObjectURL(f as unknown as File);
            if (prevObjectUrlRef.current) URL.revokeObjectURL(prevObjectUrlRef.current);
            prevObjectUrlRef.current = objUrl;
            setUrl(objUrl);
            setRes("");
            return false;
        },
        onRemove: (file) => {
            setFileList((prev) => prev.filter((it) => it.uid !== file.uid));
            setFile(null);
            if (prevObjectUrlRef.current) {
                URL.revokeObjectURL(prevObjectUrlRef.current);
                prevObjectUrlRef.current = null;
            }
            // 可恢复到默认示例图
            setUrl("https://tesseract.projectnaptha.com/img/eng_bw.png");
            setRes("");
        },
    };

    useEffect(() => {
        return () => {
            if (prevObjectUrlRef.current) URL.revokeObjectURL(prevObjectUrlRef.current);
        };
    }, []);

    return (
        <div>
            <button className="returnButton">
                <a href="/">点击返回主菜单</a>
            </button>
            <h1>tesseract</h1>
            <p>
                <a href="http://tesseract.projectnaptha.com/" target="_blank" rel="noreferrer">
                    查看官网说明
                </a>
                <br />
                <a href="https://github.com/naptha/tesseract.js/blob/master/docs/api.md">api 说明</a>
            </p>
            <div>
                <div>
                    <Upload {...props}>
                        <Button icon={<UploadOutlined />}>Click to Upload</Button>
                    </Upload>
                </div>
                {url && (
                    <div style={{ marginTop: 12 }}>
                        <img src={url} alt="预览" style={{ maxWidth: 400, border: "1px solid #ddd" }} />
                    </div>
                )}
                <div>
                    <button onClick={detailOCR}>识别图片</button>
                    <p style={{ width: "800px", margin: "auto", fontSize: "20px" }}>{res || "识别结果将显示在这里"}</p>
                </div>
            </div>
            <div>
                <h1>基于AI翻译对api文件进行解读</h1>
                <div style={{ width: "80%", margin: "auto", textAlign: "left" }}>
                    <h2>createWorker(options): Worker</h2>
                    <p>
                        createWorker 是一个用于创建 Tesseract.js 工作器的函数。Tesseract.js 工作器是一个在 Web Worker（浏览器环境）或工作线程（Node.js 环境）中创建并管理 Tesseract
                        实例的对象。创建后，OCR 任务将通过该工作器进行处理。
                        <br />
                        <b>参数：</b>
                        <br />
                        langs：一个字符串，用于指定要下载的语言训练数据，多语言可通过数组形式指定（如 ['eng', 'chi_sim']） <br />
                        oem：一个枚举值，用于指定所使用的 OCR 引擎模式 <br />
                        options：一个包含自定义选项的对象 <br />
                        <ul>
                            <li>
                                corePath：指向包含 Tesseract.js-core 包中以下所有文件的目录路径：
                                <br />
                                <ul>
                                    <li>tesseract-core.wasm.js</li>
                                    <li>tesseract-core-simd.wasm.js</li>
                                    <li>tesseract-core-lstm.wasm.js</li>
                                    <li>tesseract-core-simd-lstm.wasm.js</li>
                                    <li>
                                        网上一些代码片段会将 corePath 设置为特定的.js 文件，这是强烈不推荐的。为了提供最佳性能和最低的网络占用，Tesseract.js
                                        需要能够在不同构建版本之间进行选择。
                                    </li>
                                </ul>
                            </li>
                            <li>langPath：下载训练数据的路径，路径末尾不要包含 /</li>
                            <li>workerPath：下载工作器脚本的路径</li>
                            <li>dataPath：在 WebAssembly 文件系统中保存训练数据的路径，一般不建议修改</li>
                            <li>cachePath：缓存训练数据的路径，在 Node 环境中更有用，在浏览器环境中仅改变 IndexDB 中的键名</li>
                            <li>
                                cacheMethod：一个字符串，用于指定缓存管理方法，可选值如下：
                                <ul>
                                    <li>write：读取缓存并写回（默认方法）</li>
                                    <li>readOnly：读取缓存但不写回</li>
                                    <li>refresh：不读取缓存但写回</li>
                                    <li>none：不读取缓存也不写回</li>
                                </ul>
                            </li>
                            <li>legacyCore：设为 true 可确保下载的任何代码都支持旧版模型（除了 LSTM 模型外）</li>
                            <li>legacyLang：设为 true 可确保下载的任何语言数据都支持旧版模型（除了 LSTM 模型外）</li>
                            <li>workerBlobURL：一个布尔值，用于定义是否对工作器脚本使用 Blob URL，默认值：true</li>
                            <li>gzip：一个布尔值，用于定义远程的训练数据是否为 gzip 压缩格式，默认值：true</li>
                            <li>logger：一个用于记录进度的函数，简单示例：m =&gt; console.log(m) </li>
                            <li>errorHandler：一个用于处理工作器错误的函数，简单示例：err =&gt; console.error(err)</li>
                        </ul>
                        config：一个包含初始化前要设置的自定义选项的对象 <br />
                        该参数允许设置 “仅初始化时可用” 的 Tesseract 参数 :
                        <ul>
                            <li>大多数 Tesseract 参数可以在工作器初始化后通过worker.setParameters 或 worker.recognize 的 options 参数进行设置。</li>
                            <li>
                                少数 Tesseract 参数（在 Tesseract 文档中称为 “仅初始化时可用” 的参数）在 Tesseract 初始化后无法修改 —— 这些参数只能通过该参数进行设置 例如
                                load_system_dawg、load_number_dawg 和 load_punc_dawg
                            </li>
                        </ul>
                    </p>

                    <h2>worker.recognize(image, options, output, jobId): Promise</h2>
                    <p>
                        worker.recognize 提供了 Tesseract.js 的核心功能，因为它执行 OCR 操作,识别图像中的文字内容、确定文字在图像中的位置等。
                        <br />
                        <div>
                            <b>参数说明：</b> <br />
                            image 参见图像格式说明获取更多细节。
                            <br />
                            options 自定义选项对象
                            <br />
                            <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
                            rectangle 用于指定图像中需识别区域的对象，应包含顶部坐标、左上角坐标、宽度和高度，详见下方示例。
                            <br />
                            output 指定返回输出格式的对象（默认仅返回文本）
                            <br />
                            <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
                            其他选项包括 blocks（json）、hocr 和 tsv
                            <br />
                            jobId 请参阅上方说明
                        </div>
                        <div>
                            <b>输出：</b>
                            <span>
                                worker.recognize 返回一个包含 jobId 和 data 属性的对象的 Promise。data 属性包含使用 output 参数指定的所有格式的输出结果。
                                <br />
                            </span>
                            <b>Note:</b>
                            <span>worker.recognize 即使未检测到文本仍会返回输出对象（输出内容仅为空）。由于判定页面为空属于有效结果，因此不会抛出异常。</span>
                            <br />
                        </div>
                    </p>

                    <h2>worker.reinitialize(langs, oem, jobId): Promise</h2>
                    <p>
                        worker.reinitialize() 方法用于使用不同的语言参数和 OEM 参数重新初始化现有的 Tesseract.js 工作进程。 <br />
                        <b>参数：</b>
                        <br />
                        langs: 指定要下载的训练数据的语言的字符串，使用数组（['eng', 'chi sim']）指定多种语言 <br />
                        oem：一个枚举，用于指示您使用的OCR引擎模式 <br />
                        config： 在初始化之前设置的自定义选项对象（参见上面的详细信息） <br />
                        jobId： 请参阅以上详情
                        <b>Note：</b>
                        使用worker从Tesseract LSTM （oem值1）切换到Tesseract Legacy （oem值0）。reinitialize（）时，工作线程必须已经包含运行Tesseract Legacy模型所需的代码。
                        在create Worker选项中设置legacy Core: true和legacy Lang: true可以确保这种情况。
                    </p>

                    <h2>worker.terminate(jobId): Promise</h2>
                    <p>worker.terminate：终止worker并进行清理</p>
                </div>
            </div>
        </div>
    );
};

export default Tesseract;
